package com.tsfyun.scm.config;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tsfyun.common.base.annotation.DuplicateSubmit;
import com.tsfyun.common.base.constant.CacheConstant;
import com.tsfyun.common.base.dto.Result;
import com.tsfyun.common.base.enums.ResultCodeEnum;
import com.tsfyun.common.base.support.HttpServletRequestWrapper;
import com.tsfyun.common.base.util.ResultUtil;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.scm.security.config.StringRedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 防止重复提交拦截器
 *
 *  add at 2020-03-15
 */
@Component
@Slf4j
public class RepeatSubmitInterceptor extends HandlerInterceptorAdapter {


    //过期时间单位为秒
    @Value("${duplicate.submit.expireTime:10}")
    private long duplicateSubmitExpireTime;

    @Autowired
    private StringRedisUtils stringRedisUtils;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            DuplicateSubmit annotation = method.getAnnotation(DuplicateSubmit.class);
            if (annotation != null) {
                if (this.isRepeatSubmit(request,method)) {
                    Result result = Result.error(ResultCodeEnum.FAIL.getCode(),"不允许重复提交，请稍后再试");
                    ResultUtil.errorBack(response,result);
                    return false;
                }
            }
            return true;
        } else {
            return super.preHandle(request, response, handler);
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        Object duplicateRedisKeyObj = request.getAttribute("duplicateRedisKey");
        Object duplicateRedisKeyContentObj = request.getAttribute("duplicateRedisKeyContent");
        Object contentObj = request.getAttribute("body");
        if(Objects.nonNull(duplicateRedisKeyObj) && Objects.nonNull(duplicateRedisKeyContentObj)
           && Objects.nonNull(contentObj)) {
            //如果是成功响应则写入redis，异常的可以重复提交,注意如果前台有上传时间戳参数，需去除掉
            JSONObject jsonObject = JSON.parseObject(contentObj.toString());
            String resultCode = jsonObject.getString("code");
            if(Objects.equals(resultCode,ResultCodeEnum.SUCCESS.getCode())) {
                stringRedisUtils.set(duplicateRedisKeyObj.toString(), duplicateRedisKeyContentObj.toString(), duplicateSubmitExpireTime, TimeUnit.SECONDS);
            }
        }
        request.removeAttribute("body");
        request.removeAttribute("duplicateRedisKeyContent");
        request.removeAttribute("duplicateRedisKey");
    }

    public boolean isRepeatSubmit(HttpServletRequest request,Method method) throws Exception {
        StringBuffer uniKey = new StringBuffer("");
        String requestUrl = request.getRequestURI();
        String contentKey = getContentKey(request,method);
        if (StringUtils.isNotEmpty(contentKey)) {
            uniKey.append(requestUrl).append(":").append(StringUtils.null2EmptyWithTrim(contentKey));
            //校验是否重复提交
            String redisKey = CacheConstant.DUPLICATE_SUBMIT + SecureUtil.md5(uniKey.toString());
            log.info("请求：{}经过md5后的key是：{}", requestUrl, redisKey);
            String content = stringRedisUtils.getToString(redisKey);
            if (StringUtils.isNotEmpty(content)) {
                log.info("请求：{}经过md5后的key是：{}，存在于redis中，重复提交", requestUrl, redisKey);
                return true;
            } else {
                //如果正常响应才需要加入到redis中
                request.setAttribute("duplicateRedisKey",redisKey);
                request.setAttribute("duplicateRedisKeyContent",uniKey.toString());
            }
        }
        return false;
    }

    public String getContentKey(HttpServletRequest httpServletRequest,Method method) throws IOException {
        String requestUrl = httpServletRequest.getRequestURI();
        String contentKey;
        HttpServletRequestWrapper wrapRequest = null;
        if (httpServletRequest instanceof HttpServletRequest) {
            wrapRequest = new HttpServletRequestWrapper(httpServletRequest);
        }
        //从参数中取
        //判断是否json
        boolean isJson = false;
        Parameter[] parameters = method.getParameters();
        if(ArrayUtil.isNotEmpty(parameters)) {
            long jsonCount = Arrays.asList(parameters).stream().filter(r->r.isAnnotationPresent(RequestBody.class)).count();
            if(jsonCount > 0) {
                isJson = true;
            }
        }
        if(isJson) {
            HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(httpServletRequest);
            contentKey = wrapper.getRequestBodyParame();
            log.info("请求：{}是json请求，参数的值是：{}",requestUrl,contentKey);
        } else {
            Map<String, String[]> paramMap =  wrapRequest.getParameterMap();
            contentKey = JSON.toJSONString(paramMap);
            log.info("请求：{}不是json请求，参数的值是：{}",requestUrl,contentKey);
        }
        return contentKey;
    }


}
